﻿using Newtonsoft.Json;
using Serilog;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Helpers
{
    public static class SERedisConnectionMultiplexerHelper
    {
        private static readonly object _locker = new Object();

        private static readonly ConcurrentDictionary<string, Lazy<ConnectionMultiplexer>> _dictionary = new ConcurrentDictionary<string, Lazy<ConnectionMultiplexer>>();
        static SERedisConnectionMultiplexerHelper()
        {
            //System.Threading.ThreadPool.SetMinThreads(200, 200);
        }

        /// <summary>
        /// 高并发下提前初始化redis
        /// </summary>
        /// <param name="confs"></param>
        public static void Init(params string[] confs)
        {
            foreach (var conf in confs)
            {
                GetConnectionMultiplexer(conf);
            }
        }

        public static ConnectionMultiplexer GetConnectionMultiplexer(string configuration)
        {
            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }
            ConnectionMultiplexer cm = null;
            try
            {
                cm = GetOrAdd(configuration);
            }
            catch (Exception ex)
            {
                Log.Information(ex.ToString());
            }
            return cm;
        }

        private static ConnectionMultiplexer GetOrAdd(string configuration)
        {
            try
            {
                var cm = _dictionary.GetOrAdd(configuration, it => new Lazy<ConnectionMultiplexer>(() =>
                      {
                          var temp = ConnectionMultiplexer.Connect(configuration, Console.Out);

                          temp.ConnectionFailed += Temp_ConnectionFailed;
                          temp.ConfigurationChanged += Temp_ConfigurationChanged;
                          temp.ConfigurationChangedBroadcast += Temp_ConfigurationChangedBroadcast;
                          temp.ErrorMessage += Temp_ErrorMessage;
                          temp.InternalError += Temp_InternalError;
                          temp.ConnectionRestored += Temp_ConnectionRestored;
                          temp.HashSlotMoved += Temp_HashSlotMoved;
                          return temp;
                      })).Value;
                return cm;
            }
            catch (Exception)
            {
                _dictionary.TryRemove(configuration, out _);
                throw;
            }

            //if (!cm.IsConnected)
            //{
            //    lock (_locker)
            //    {
            //        cm = _dictionary.GetOrAdd(configuration, it => new Lazy<ConnectionMultiplexer>(() =>
            //        {
            //            LoggerHelper.LogInformation($"{DateTime.Now} 创建redis链接成功,配置是{configuration}");
            //            return ConnectionMultiplexer.Connect(configuration);
            //        })).Value;
            //        if (!cm.IsConnected)
            //        {
            //            LoggerHelper.LogInformation($"{DateTime.Now} redis已经断开链接,配置是{configuration}");
            //            if (_dictionary.TryRemove(configuration, out var _))
            //            {
            //                cm.Dispose();
            //                cm = _dictionary.GetOrAdd(configuration, it => new Lazy<ConnectionMultiplexer>(() =>
            //                {
            //                    LoggerHelper.LogInformation($"{DateTime.Now} 重新创建redis链接成功,配置是{configuration}");
            //                    return ConnectionMultiplexer.Connect(configuration);
            //                })).Value;
            //            }
            //            else
            //            {
            //                LoggerHelper.LogInformation($"{DateTime.Now} 移除redis链接失败,配置是{configuration}");
            //            }
            //        }
            //    }
            //}
            //return cm;
        }

        private static void Temp_HashSlotMoved(object sender, HashSlotMovedEventArgs e)
        {
            Log.Information($"{DateTime.Now} {nameof(Temp_HashSlotMoved)} {e.HashSlot} {e.OldEndPoint} {e.NewEndPoint}");
        }

        private static void Temp_ConnectionRestored(object sender, ConnectionFailedEventArgs e)
        {
            Log.Information($"{DateTime.Now} {nameof(Temp_ConnectionRestored)} {e.FailureType} {e.ConnectionType} {e.Exception}");
        }

        private static void Temp_InternalError(object sender, InternalErrorEventArgs e)
        {
            Log.Information($"{DateTime.Now} {nameof(Temp_InternalError)} {e.ConnectionType} {e.EndPoint} {e.Origin} {e.Exception}");
        }

        private static void Temp_ErrorMessage(object sender, RedisErrorEventArgs e)
        {
            Log.Information($"{DateTime.Now} {nameof(Temp_ErrorMessage)} {e.EndPoint} {e.Message}");
        }

        private static void Temp_ConfigurationChangedBroadcast(object sender, EndPointEventArgs e)
        {
            Log.Information($"{DateTime.Now} {nameof(Temp_ConfigurationChangedBroadcast)} {e.EndPoint}");
        }

        private static void Temp_ConfigurationChanged(object sender, EndPointEventArgs e)
        {
            Log.Information($"{DateTime.Now} {nameof(Temp_ConfigurationChanged)} {e.EndPoint}");
        }

        private static void Temp_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
            Log.Information($"{DateTime.Now} {nameof(Temp_ConnectionFailed)} {e.FailureType} {e.ConnectionType} {e.Exception}");
        }
    }

    /// <summary>
    /// Redis操作
    /// </summary>
    public class SERedisHelper
    {
        /// <summary>
        /// 数据库序号，默认是0号数据库
        /// </summary>
        private int DbNum { get; }
        /// <summary>
        /// 自定义的key前缀
        /// </summary>
        public string PrefixKey { get; }
        public static SERedisHelper Instance = new SERedisHelper();
        private readonly string _config;

        private ConnectionMultiplexer ConnectionMultiplexer => SERedisConnectionMultiplexerHelper.GetConnectionMultiplexer(_config);
        public ITransaction MyTransaction => MyDatabase.CreateTransaction();

        public IDatabase MyDatabase => ConnectionMultiplexer.GetDatabase(DbNum);

        public IBatch MyBatch => MyDatabase.CreateBatch();
        public ISubscriber MySubscriber => ConnectionMultiplexer.GetSubscriber();

        public SERedisHelper() : this("", 0, "127.0.0.1:6379,abortConnect=false,ssl=false,allowAdmin=true,syncTimeout=30000,ConnectTimeout=10000")
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="prefixKey">前缀</param>
        /// <param name="dbNum">数据库</param>
        /// <param name="config">配置</param>
        public SERedisHelper(string prefixKey, int dbNum, string config)
        {
            DbNum = dbNum;
            PrefixKey = prefixKey;
            _config = config;
        }

        public SERedisHelper(string prefixKey, string config) : this(prefixKey, 0, config)
        {
        }

        #region String

        #region 同步方法

        /// <summary>
        /// 保存单个key value
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public bool StringSet(string key, string value, TimeSpan? expiry = default)
        {
            key = MergeKey(key);
            return Do(db => db.StringSet(key, value, expiry));
        }

        /// <summary>
        /// 保存多个key value
        /// </summary>
        /// <param name="keyValues">键值对</param>
        /// <returns></returns>
        public bool StringSet(List<KeyValuePair<RedisKey, RedisValue>> keyValues)
        {
            List<KeyValuePair<RedisKey, RedisValue>> newkeyValues =
                keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(MergeKey(p.Key), p.Value)).ToList();
            return Do(db => db.StringSet(newkeyValues.ToArray()));
        }

        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public bool StringSet<T>(string key, T obj, TimeSpan? expiry = default)
        {
            key = MergeKey(key);
            string json = ConvertJson(obj);
            return Do(db => db.StringSet(key, json, expiry));
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>
        public string StringGet(string key)
        {
            key = MergeKey(key);
            return Do(db => db.StringGet(key));
        }

        /// <summary>
        /// 获取多个Key
        /// </summary>
        /// <param name="listKey">Redis Key集合</param>
        /// <returns></returns>
        public RedisValue[] StringGet(List<string> listKey)
        {
            List<string> newKeys = listKey.Select(MergeKey).ToList();
            return Do(db => db.StringGet(ConvertRedisKeys(newKeys)));
        }

        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T StringGet<T>(string key) where T : class
        {
            key = MergeKey(key);
            return Do(db => ConvertToObj<T>(db.StringGet(key)));
        }

        /// <summary>
        /// 为数字增长val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val">可以为负</param>
        /// <returns>增长后的值</returns>
        public double StringIncrement(string key, double val = 1)
        {
            key = MergeKey(key);
            return Do(db => db.StringIncrement(key, val));
        }

        /// <summary>
        /// 为数字减少val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val">可以为负</param>
        /// <returns>减少后的值</returns>
        public double StringDecrement(string key, double val = 1)
        {
            key = MergeKey(key);
            return Do(db => db.StringDecrement(key, val));
        }

        #endregion 同步方法

        #region 异步方法

        /// <summary>
        /// 保存单个key value
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <param name="value">保存的值</param>
        /// <param name="expiry">过期时间</param>
        /// <returns></returns>
        public async Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry = null, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            key = MergeKey(key);
            return await Do(db => db.StringSetAsync(key, value, expiry, when, flags));
        }

        /// <summary>
        /// 保存多个key value
        /// </summary>
        /// <param name="keyValues">键值对</param>
        /// <returns></returns>
        public async Task<bool> StringSetAsync(List<KeyValuePair<RedisKey, RedisValue>> keyValues)
        {
            List<KeyValuePair<RedisKey, RedisValue>> newkeyValues =
                keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(MergeKey(p.Key), p.Value)).ToList();
            return await Do(db => db.StringSetAsync(newkeyValues.ToArray()));
        }

        /// <summary>
        /// 保存一个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public async Task<bool> StringSetAsync<T>(string key, T obj, TimeSpan? expiry = default)
        {
            key = MergeKey(key);
            string json = ConvertJson(obj);
            return await Do(db => db.StringSetAsync(key, json, expiry));
        }

        /// <summary>
        /// 获取单个key的值
        /// </summary>
        /// <param name="key">Redis Key</param>
        /// <returns></returns>
        public async Task<string> StringGetAsync(string key)
        {
            key = MergeKey(key);
            return await Do(db => db.StringGetAsync(key));
        }

        /// <summary>
        /// 获取多个Key
        /// </summary>
        /// <param name="listKey">Redis Key集合</param>
        /// <returns></returns>
        public async Task<RedisValue[]> StringGetAsync(List<string> listKey)
        {
            List<string> newKeys = listKey.Select(MergeKey).ToList();
            return await Do(db => db.StringGetAsync(ConvertRedisKeys(newKeys)));
        }

        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> StringGetAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            string result = await Do(db => db.StringGetAsync(key));
            return ConvertToObj<T>(result);
        }

        /// <summary>
        /// 为数字增长val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val">可以为负</param>
        /// <returns>增长后的值</returns>
        public async Task<double> StringIncrementAsync(string key, double val = 1)
        {
            key = MergeKey(key);
            return await Do(db => db.StringIncrementAsync(key, val));
        }
        public async Task<long> StringIncrementAsync(string key, long val = 1)
        {
            key = MergeKey(key);
            return await Do(db => db.StringIncrementAsync(key, val));
        }

        /// <summary>
        /// 为数字减少val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val">可以为负</param>
        /// <returns>减少后的值</returns>
        public async Task<double> StringDecrementAsync(string key, double val = 1)
        {
            key = MergeKey(key);
            return await Do(db => db.StringDecrementAsync(key, val));
        }
        public async Task<long> StringDecrementAsync(string key, long val = 1)
        {
            key = MergeKey(key);
            return await Do(db => db.StringDecrementAsync(key, val));
        }

        /// <summary>
        /// 限流,限制key在指定时间内访问的次数
        /// </summary>
        /// <param name="key">主键</param>
        /// <param name="expiry">过期时间</param>
        /// <param name="limit">上限</param>
        /// <returns>返回是否超过使用限制和当前的使用次数</returns>
        public async Task<(bool isAllow, long count)> ThrottleAsync(string key, TimeSpan expiry, int limit)
        {
            var value = await StringIncrementAsync(key, 1);
            if (value == 1)
            {
                await KeyExpireAsync(key, DateTime.Now.Add(expiry));
            }

            return (value <= limit, value);
        }

        #endregion 异步方法

        #endregion String

        #region Hash

        /// <summary>
        /// 判断某个数据是否已经被缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public Task<bool> HashExistsAsync(string key, string dataKey)
        {
            key = MergeKey(key);
            return Do(db => db.HashExistsAsync(key, dataKey));
        }

        public async Task<Dictionary<string, T>> HashGetAllAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            var value = await Do(db => db.HashGetAllAsync(key));
            return ConvetToDictionary<T>(value);

        }

        public async Task<IEnumerable<T>> HashValuesAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            var value = await Do(db => db.HashValuesAsync(key));
            return ConvetToList<T>(value);
        }

        /// <summary>
        /// 存储数据到hash表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public Task<bool> HashSetAsync<T>(string key, string dataKey, T t)
        {
            key = MergeKey(key);
            return Do(db =>
            {
                string json = ConvertJson(t);
                return db.HashSetAsync(key, dataKey, json);
            });
        }

        /// <summary>
        /// 移除hash中的某值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public Task<bool> HashDeleteAsync(string key, string dataKey)
        {
            key = MergeKey(key);
            return Do(db => db.HashDeleteAsync(key, dataKey));
        }

        /// <summary>
        /// 移除hash中的多个值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKeys"></param>
        /// <returns></returns>
        public Task<long> HashDeleteAsync(string key, List<RedisValue> dataKeys)
        {
            key = MergeKey(key);
            //List<RedisValue> dataKeys1 = new List<RedisValue>() {"1","2"};
            return Do(db => db.HashDeleteAsync(key, dataKeys.ToArray()));
        }

        /// <summary>
        /// 从hash表获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <returns></returns>
        public async Task<T> HashGetAsync<T>(string key, string dataKey) where T : class
        {
            key = MergeKey(key);
            string value = await Do(db => db.HashGetAsync(key, dataKey));
            return ConvertToObj<T>(value);
        }
        public T HashGet<T>(string key, string dataKey) where T : class
        {
            key = MergeKey(key);
            string value = Do(db => db.HashGet(key, dataKey));
            return ConvertToObj<T>(value);
        }

        /// <summary>
        /// 为数字增长val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>增长后的值</returns>
        public Task<double> HashIncrementAsync(string key, string dataKey, double val = 1)
        {
            key = MergeKey(key);
            return Do(db => db.HashIncrementAsync(key, dataKey, val));
        }

        /// <summary>
        /// 为数字减少val
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="val">可以为负</param>
        /// <returns>减少后的值</returns>
        public Task<double> HashDecrementAsync(string key, string dataKey, double val = 1)
        {
            key = MergeKey(key);
            return Do(db => db.HashDecrementAsync(key, dataKey, val));
        }

        /// <summary>
        /// 获取hashkey所有Redis key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<IEnumerable<T>> HashKeysAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            RedisValue[] values = await Do(db => db.HashKeysAsync(key));
            return ConvetToList<T>(values);
        }


        #endregion Hash

        #region List

        /// <summary>
        /// 移除指定ListId的内部List的值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public async Task<long> ListRemoveAsync<T>(string key, T value)
        {
            key = MergeKey(key);
            return await Do(db => db.ListRemoveAsync(key, ConvertJson(value)));
        }

        /// <summary>
        /// 限定list的长度
        /// </summary>
        public Task ListTrimAsync(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None)
        {
            key = MergeKey(key);
            return Do(db => db.ListTrimAsync(key, start, stop, flags));
        }

        /// <summary>
        /// 入队
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public async Task<long> ListRightPushAsync<T>(string key, T value)
        {
            key = MergeKey(key);
            return await Do(db => db.ListRightPushAsync(key, ConvertJson(value)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> ListRightPopAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            var value = await Do(db => db.ListRightPopAsync(key));
            return ConvertToObj<T>(value);
        }

        //public T ListRightPop<T>(string key) where T : class
        //{
        //    key = AddSysCustomKey(key);
        //    var value = Do(db => db.ListRightPop(key));
        //    return ConvertToObj<T>(value);
        //}

        /// <summary>
        /// 添加到头部
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public Task<long> ListLeftPushAsync<T>(string key, T value, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            if (value == null)
            {
                return Task.FromResult((long)-1);
            }

            key = MergeKey(key);
            return Do(db => db.ListLeftPushAsync(key, ConvertJson(value), when, flags));
        }

        public async Task<IEnumerable<T>> ListRangeAsync<T>(RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None) where T : class
        {
            key = MergeKey(key);
            return ConvetToList<T>(await Do(db => db.ListRangeAsync(key, start, stop, flags)));
        }

        /// <summary>
        /// 出栈,出队
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<T> ListLeftPopAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            var value = await Do(db => db.ListLeftPopAsync(key));
            return ConvertToObj<T>(value);
        }
        public T ListLeftPop<T>(string key) where T : class
        {
            key = MergeKey(key);
            var value = Do(db => db.ListLeftPop(key));
            return ConvertToObj<T>(value);
        }


        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> ListLengthAsync(string key)
        {
            key = MergeKey(key);
            return await Do(redis => redis.ListLengthAsync(key));
        }

        #endregion List

        #region SortedSet 有序集合

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score"></param>
        public async Task<bool> SortedSetAddAsync<T>(string key, T value, double score, When when = When.Always, CommandFlags flags = CommandFlags.None)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SortedSetAddAsync(key, ConvertJson<T>(value), score, when, flags));
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public async Task<bool> SortedSetRemoveAsync<T>(string key, T value)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SortedSetRemoveAsync(key, ConvertJson(value)));
        }

        public Task<double> SortedSetIncrementAsync(string key, string member, double value, CommandFlags flags = CommandFlags.None)
        {
            key = MergeKey(key);
            return Do(redis => redis.SortedSetIncrementAsync(key, member, value, flags));
        }

        public Task<double> SortedSetDecrementAsync(string key, string member, double value)
        {
            key = MergeKey(key);
            return Do(redis => redis.SortedSetDecrementAsync(key, member, value));
        }


        /// <summary>
        /// 获取集合中的数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> SortedSetLengthAsync(string key)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SortedSetLengthAsync(key));
        }

        public async Task<long> SortedSetLengthByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SortedSetLengthByValueAsync(key, min, max, exclude));
        }

        public async Task<IEnumerable<T>> SortedSetRangeByRankAsync<T>(string key, long start = 0, long stop = -1, Order order = Order.Ascending) where T : class
        {
            key = MergeKey(key);
            var values = await Do(redis => redis.SortedSetRangeByRankAsync(key, start, stop, order));
            return ConvetToList<T>(values);
        }

        public async Task<SortedSetEntry[]> SortedSetRangeByRankWithScoresAsync(string key, long start = 0, long stop = -1, Order order = Order.Ascending)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SortedSetRangeByRankWithScoresAsync(key, start, stop, order));
        }

        public async Task<RedisValue[]> SortedSetRangeByScoreAsync(string key, double start, double stop, Exclude exclude = Exclude.None)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SortedSetRangeByScoreAsync(key, start, stop, exclude));
        }
        public async Task<SortedSetEntry[]> SortedSetRangeByScoreWithScoresAsync(string key, double start, double stop, Exclude exclude = Exclude.None)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SortedSetRangeByScoreWithScoresAsync(key, start, stop, exclude));
        }

        #endregion SortedSet 有序集合

        #region set集合

        public async Task<IEnumerable<T>> SetRandomMembersAsync<T>(RedisKey key, long count, CommandFlags flags = CommandFlags.None) where T : class
        {
            if (count <= 0)
            {
                return Array.Empty<T>();
            }

            key = MergeKey(key);
            return ConvetToList<T>(await Do(redis => redis.SetRandomMembersAsync(key, count, flags)));
        }

        public async Task<IEnumerable<T>> SetMembersAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            return ConvetToList<T>(await Do(db => db.SetMembersAsync(key)));
        }

        public async Task<long> SetLengthAsync(string key)
        {
            key = MergeKey(key);
            return await Do(db => db.SetLengthAsync(key));
        }

        public Task<bool> SetContainsAsync<T>(string key, T value)
        {
            key = MergeKey(key);
            return Do(redis => redis.SetContainsAsync(key, ConvertJson<T>(value)));
        }

        public Task<bool> SetAddAsync<T>(string key, T value, CommandFlags flags = CommandFlags.None)
        {
            key = MergeKey(key);
            return Do(redis => redis.SetAddAsync(key, ConvertJson<T>(value), flags));
        }

        public Task<bool> SetRemoveAsync<T>(string key, T value, CommandFlags flags = CommandFlags.None)
        {
            key = MergeKey(key);
            return Do(redis => redis.SetRemoveAsync(key, ConvertJson<T>(value), flags));
        }

        public async Task<T> SetPopAsync<T>(string key) where T : class
        {
            key = MergeKey(key);
            return ConvertToObj<T>(await Do(redis => redis.SetPopAsync(key)));
        }

        public async Task<string> SetRandomMemberAsync(string key)
        {
            key = MergeKey(key);
            return await Do(redis => redis.SetRandomMemberAsync(key));
        }

        #endregion

        #region key

        /// <summary>
        /// 搜索所有匹配关键字，*代表搜索所有键
        /// </summary>
        public async Task<string[]> SearchKeys(string pattern)
        {
            pattern = MergeKey(pattern);
            var keys = new HashSet<string>();
            int nextCursor = 0;
            do
            {
                RedisResult redisResult = await Do(it => it.ExecuteAsync("SCAN", nextCursor.ToString(), "MATCH", pattern, "COUNT", "1000"));
                var innerResult = (RedisResult[])redisResult;
                nextCursor = int.Parse((string)innerResult[0]);
                var resultLines = (string[])innerResult[1];
                keys.UnionWith(resultLines);
            }
            while (nextCursor != 0);
            return keys.Select(it => it.Substring(PrefixKey.Length)).ToArray();
        }

        public Task<long> KeyDeleteAsync(params string[] keys)
        {
            return Do(db => db.KeyDeleteAsync(keys.Select(it => (RedisKey)MergeKey(it)).ToArray()));
        }


        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public bool KeyExists(string key)
        {
            key = MergeKey(key);
            return Do(db => db.KeyExists(key));
        }

        /// <summary>
        /// 判断key是否存储
        /// </summary>
        /// <param name="key">redis key</param>
        /// <returns></returns>
        public Task<bool> KeyExistsAsync(string key)
        {
            key = MergeKey(key);
            return Do(db => db.KeyExistsAsync(key));
        }

        /// <summary>
        /// 重新命名key
        /// </summary>
        /// <param name="key">就的redis key</param>
        /// <param name="newKey">新的redis key</param>
        /// <returns></returns>
        public bool KeyRename(string key, string newKey)
        {
            key = MergeKey(key);
            return Do(db => db.KeyRename(key, newKey));
        }

        /// <summary>
        /// 设置Key的时间
        /// </summary>
        /// <param name="key">redis key</param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public Task<bool> KeyExpireAsync(string key, TimeSpan? expiry = null, CommandFlags flags = CommandFlags.None)
        {
            key = MergeKey(key);
            return Do(db => db.KeyExpireAsync(key, expiry, flags));
        }

        public Task<bool> KeyExpireAsync(string key, DateTime? expiry = null)
        {
            key = MergeKey(key);
            return Do(db => db.KeyExpireAsync(key, expiry));
        }

        #endregion key

        #region 发布订阅

        /// <summary>
        /// Redis发布订阅  订阅
        /// </summary>
        /// <param name="subChannel"></param>
        /// <param name="handler"></param>
        public void Subscribe(string subChannel, Action<RedisChannel, RedisValue> handler = null)
        {
            MySubscriber.Subscribe(subChannel, (channel, message) =>
            {
                if (handler == null)
                {
                    Log.Information(subChannel + " 订阅收到消息：" + message);
                }
                else
                {
                    handler(channel, message);
                }
            });
        }

        /// <summary>
        /// Redis发布订阅  发布
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="channel"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public long Publish<T>(string channel, T msg)
        {
            return MySubscriber.Publish(channel, ConvertJson(msg));
        }

        /// <summary>
        /// Redis发布订阅  取消订阅
        /// </summary>
        /// <param name="channel"></param>
        public void Unsubscribe(string channel)
        {
            MySubscriber.Unsubscribe(channel);
        }

        /// <summary>
        /// Redis发布订阅  取消全部订阅
        /// </summary>
        public void UnsubscribeAll()
        {
            MySubscriber.UnsubscribeAll();
        }

        #endregion 发布订阅

        #region 锁
        public bool LockTake(string key, string value, TimeSpan? expiry = null)
        {
            return Do(redis => redis.LockTake(key, value, expiry ?? TimeSpan.FromSeconds(1)));
        }

        public Task<bool> LockReleaseAsync(string key, string value)
        {
            return Do(redis => redis.LockReleaseAsync(key, value));
        }

        #endregion

        #region 其他



        public IServer GetServer(string hostAndPort) => ConnectionMultiplexer.GetServer(hostAndPort);

        #endregion 其他

        #region 辅助方法

        internal string MergeKey(string key)
        {

            if (Regex.IsMatch(key, PrefixKey))
            {
                return key;
            }
            else
            {
                return PrefixKey + key;
            }
        }


        private T Do<T>(Func<IDatabase, T> func)
        {
            return func(MyDatabase);
        }

        internal string ConvertJson<T>(T value)
        {
            if (value == null)
            {
                return null;
            }

            string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value);
            return result;
        }

        internal T ConvertToObj<T>(RedisValue value) where T : class
        {
            if (value.HasValue)
            {
                return typeof(T).Name == typeof(string).Name ? value.ToString() as T : JsonConvert.DeserializeObject<T>(value);
            }
            else
            {
                return default;
            }
        }

        internal IEnumerable<T> ConvetToList<T>(RedisValue[] values) where T : class
        {
            return values?.Select(it => ConvertToObj<T>(it));
        }


        internal Dictionary<string, T> ConvetToDictionary<T>(HashEntry[] values) where T : class
        {
            if (values == null || values.Length == 0)
            {
                return null;
            }

            Dictionary<string, T> result = new Dictionary<string, T>();
            foreach (var item in values)
            {
                var model = ConvertToObj<T>(item.Value);
                result.Add(item.Name, model);
            }
            return result;
        }

        private RedisKey[] ConvertRedisKeys(List<string> redisKeys)
        {
            return redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();
        }

        #endregion 辅助方法
    }

    /// <summary>
    /// redis批处理
    /// </summary>
    public class SERedisBatchHelper
    {
        private readonly IBatch _batch;
        private readonly SERedisHelper _redisHelper;

        public SERedisBatchHelper(SERedisHelper redisHelper)
        {
            _redisHelper = redisHelper;
            _batch = redisHelper.MyBatch;
        }

        #region zset
        public Task SortedSetAddAsync(RedisKey key, RedisValue member, double score, CommandFlags flags = CommandFlags.FireAndForget)
        {
            return _batch.SortedSetAddAsync(MergeKey(key), member, score, flags);
        }

        public Task<double> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None)
        {
            return _batch.SortedSetIncrementAsync(MergeKey(key), member, value, flags);
        }
        #endregion

        #region hashSet
        public Task HashSetAsync<T>(RedisKey key, RedisValue hashField, T value, When when = When.Always, CommandFlags flags = CommandFlags.FireAndForget)
        {
            return _batch.HashSetAsync(_redisHelper.MergeKey(key), hashField, _redisHelper.ConvertJson(value), when, flags);
        }
        #endregion

        public void Execute()
        {
            _batch.Execute();
        }

        private string MergeKey(string key) => _redisHelper.MergeKey(key);
    }

}